package br.uninove.ic.texture;

import br.uninove.ic.texture.utils.ImagesUtils;

public class MatrizHistogramaSomaDiferenca {

    private IGrayScaleImage img;
    private int distancia;
    private int angulo;
    private double[] hSoma;
    private double[] hDiferenca;

    public MatrizHistogramaSomaDiferenca(IGrayScaleImage img, int distancia,
	    int angulo) {
	this.img = img;
	this.distancia = distancia;
	this.angulo = angulo;
	getMatriz();
    }

    public void getMatriz() {
	hSoma = new double[256 * 2];
	hDiferenca = new double[256];

	for (int x = 0; x < img.getWidth(); x++) {
	    for (int y = 0; y < img.getHeight(); y++) {

		switch (angulo) {
		case 0:
		    if (ImagesUtils.isIndexImage(img, x, y)
			    && ImagesUtils.isIndexImage(img, x + distancia, y)) {
			hSoma[img.getPixel(x, y)
				+ img.getPixel(x + distancia, y)]++;
			hDiferenca[Math.abs(img.getPixel(x, y)
				- img.getPixel(x + distancia, y))]++;
		    }
		    break;
		case 45:
		    if (ImagesUtils.isIndexImage(img, x, y)
			    && ImagesUtils.isIndexImage(img, x + distancia, y
				    + distancia)) {
			hSoma[img.getPixel(x, y)
				+ img.getPixel(x + distancia, y + distancia)]++;
			hDiferenca[Math.abs(img.getPixel(x, y)
				- img.getPixel(x + distancia, y + distancia))]++;
		    }
		    break;
		case 90:
		    if (ImagesUtils.isIndexImage(img, x, y)
			    && ImagesUtils.isIndexImage(img, x, y + distancia)) {
			hSoma[img.getPixel(x, y)
				+ img.getPixel(x, y + distancia)]++;
			hDiferenca[Math.abs(img.getPixel(x, y)
				- img.getPixel(x, y + distancia))]++;
		    }
		    break;
		case 135:
		    if (ImagesUtils.isIndexImage(img, x, y)
			    && ImagesUtils.isIndexImage(img, x - distancia, y
				    + distancia)) {
			hSoma[img.getPixel(x, y)
				+ img.getPixel(x - distancia, y + distancia)]++;
			hDiferenca[Math.abs(img.getPixel(x, y)
				- img.getPixel(x - distancia, y + distancia))]++;
		    }
		    break;
		}
	    }
	}
    }

    public double contraste() {
	double c = 0;
	for (int j = 0; j < hDiferenca.length; j++) {
	    c += (Math.pow(j, 2) * hDiferenca[j]);
	}
	return c;
    }

    public double entropia() {
	double etSoma = 0;
	double etDif = 0;
	final double E = 0.000000000000000001;
	for (int i = 0; i < hSoma.length; i++) {
	    etSoma += ((Math.log(hSoma[i] + E) / Math.log(2)) * hSoma[i]);
	}

	for (int i = 0; i < hDiferenca.length; i++) {
	    etDif += ((Math.log(hDiferenca[i] + E) / Math.log(2)) * hDiferenca[i]);
	}
	return (-etSoma - etDif);
    }

    public double momentoDaDiferencaInversa() {
	double h = 0;
	for (int j = 0; j < hDiferenca.length; j++) {
	    h += ((1 / (1 + Math.pow(j, 2))) * hDiferenca[j]);
	}
	return h;
    }

    public double correlacao() {
	double crhSoma = 0;
	double crhDif = 0;
	double media = media();

	for (int i = 0; i < hSoma.length; i++) {
	    crhSoma += (Math.pow((i - (2 * media)), 2) * hSoma[i]);
	}
	for (int j = 0; j < hDiferenca.length; j++) {
	    crhDif += (Math.pow(j, 2) * hDiferenca[j]);
	}

	return (1 / 2 * crhSoma - crhDif);
    }

    public double variancia() {
	double vhSoma = 0;
	double vhDif = 0;
	double media = media();

	for (int i = 0; i < hSoma.length; i++) {
	    vhSoma += (Math.pow((i - (2 * media)), 2) * hSoma[i]);
	}
	for (int j = 0; j < hDiferenca.length; j++) {
	    vhDif += (Math.pow(j, 2) * hDiferenca[j]);
	}

	return (1 / 2 * vhSoma + vhDif);
    }

    public double media() {
	double media = 0;
	for (int i = 0; i < hSoma.length; i++) {
	    media += hSoma[i];
	}
	return media;
    }

    public double segundoMomentoAngular() {
	double ens = 0;
	double end = 0;

	for (int i = 0; i < hSoma.length; i++) {
	    ens += (hSoma[i]);
	}
	for (int j = 0; j < hDiferenca.length; j++) {
	    end += (hDiferenca[j]);
	}

	return (ens * end);
    }
}
